﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace UtilsLib.UtilsThreading
{
    public static class TaskManager
    {
        public static List<Task> Tasks { get; set; }
        private static int nbTasks;
        private static int NbMaxTask;
        private static bool LimitedTasks;

        /// <summary>
        /// Initialise the TaskManager
        /// </summary>
        /// <param name="nbMaxTask">The max number of concurrent tasks</param>
        public static void InitManager(int nbMaxTask=0)
        {
            nbTasks = 0;
            Tasks = new List<Task>();
            NbMaxTask = nbMaxTask;
            LimitedTasks = !(NbMaxTask == 0);
        }

        /// <summary>
        /// Wait for all task to finish
        /// </summary>
        public static void JoinAll()
        {
            Task.WaitAll(Tasks.ToArray());
            nbTasks = 0;
        }

        /// <summary>
        /// Add a new task execution.
        /// </summary>
        /// <param name="obj">An instance of the object containing the method</param>
        /// <param name="methodName">The name of the method to execute in the task</param>
        /// <param name="param">The parameters of the method</param>
        public static void AddTask(Object obj, string methodName, Object[] param)
        {
            int index = 0;
            while (index < Tasks.Count)
                if (Tasks[index].IsCompleted)
                {
                    Tasks.RemoveAt(index);
                    nbTasks--;
                }
                else
                    index++;

            if ((LimitedTasks) && (NbMaxTask <= nbTasks))
                Task.WaitAny(Tasks.ToArray());

            
            Tasks.Add(Task.Factory.StartNew(() =>
                {
                    Type objType = obj.GetType();
                    MethodInfo method = objType.GetMethod(methodName);
                    method.Invoke(obj, param);
                }));
            nbTasks++;
        }

        /// <summary>
        /// Add a new task execution.
        /// </summary>
        /// <param name="obj">An instance of the object containing the method</param>
        /// <param name="methodName">The name of the method to execute in the task</param>
        /// <param name="param">The parameters of the method</param>
        public static void AddTask(Object obj, string methodName, List<Object> param)
        {
            Object[] parameters = new Object[param.Count];
            for (int i = 0; i < param.Count; i++)
            {
                parameters[i] = param[i];
            }
            AddTask(obj, methodName, parameters);
        }
    }
}
